package sk.fei.vrml.jogl;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import javax.media.opengl.GL;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

import sk.fei.vrml.objects.VrmlObject;
import sk.fei.vrml.objects.VrmlTransform;
import sk.fei.vrml.objects.VrmlViewPoint;
import sk.fei.vrml.objects.VrmlViewPointsStorage;

public class JoglEventListener implements GLEventListener{
	private GLCanvas canvas;
	private int rotateYAngle,rotateXAngle,rotateZAngle;
	private int xMove,yMove,zMove;
	private int moveSize;
	private int angleSize;
	public ArrayList <VrmlViewPoint> viewpoints=new ArrayList<VrmlViewPoint>();
	
	public void setRotateYAngle(int rotateYAngle) {
		this.rotateYAngle = rotateYAngle;
	}

	public void setRotateXAngle(int rotateXAngle) {
		this.rotateXAngle = rotateXAngle;
	}

	public void setRotateZAngle(int rotateZAngle) {
		this.rotateZAngle = rotateZAngle;
	}

	public void setxMove(int xMove) {
		this.xMove = xMove;
	}

	public void setyMove(int yMove) {
		this.yMove = yMove;
	}

	public void setzMove(int zMove) {
		this.zMove = zMove;
	}
	private VrmlModel activModel;
	

	
	public JoglEventListener(GLCanvas canvas){
		this.canvas = canvas;
		this.rotateXAngle = 0;
		this.rotateYAngle = 0;
		this.rotateZAngle = 0;
		this.xMove=0;
		this.yMove=0;
		this.zMove=0;
		angleSize = 10;
		moveSize = 1;
	}

	public void display(GLAutoDrawable GLAutoDrawable) {
		 final GL gl = GLAutoDrawable.getGL();
		 gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		 
			 gl.glDisable(GL.GL_DEPTH_TEST);
			 gl.glDisable(GL.GL_LIGHTING);
			 gl.glMatrixMode(GL.GL_MODELVIEW);
			 gl.glLoadIdentity();
		 
		// Pozadie
		 if (activModel.isBackground()){
			 activModel.getModelBackground().draw(GLAutoDrawable);	
		 }
		 // Koniec pozadia
		 
		// gl.glMatrixMode(GL.GL_MODELVIEW);
		// gl.glLoadIdentity();// koli performance by bolo vhodne hodit do init metody od tialto
		//TODO tu je vylepsenie pohladu na scenu + posunutie zakladneho viewpointu do pozicie ako je vo vrealme
		 gl.glEnable(GL.GL_BLEND);
         gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
         gl.glEnable( GL.GL_DEPTH_TEST );
         gl.glDepthFunc(GL.GL_LEQUAL);
         
         gl.glEnable(GL.GL_LIGHTING); // po tialto ale blbne to nvm preco
		 // nastavenie svetla v scene
         float[] noAmbient = { 0.0f, 0.0f, 0.0f, 1f }; 
		 float[] lightPos = { 0,1,-1,1};			
		 float[] ambient = { 0.2f, 0.2f, 0.2f, 1f }; 	
		 float[] diffuse =  { 0.5f,  0.5f,  0.5f, 1f };
		 float[] specular = {0.0f,0.0f,0.0f,1.0f};
		 gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT,  FloatBuffer.wrap(ambient));
		// gl.glLightModeli(GL.GL_LIGHT_MODEL_AMBIENT, GL.GL_TRUE);
		 gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, FloatBuffer.wrap(noAmbient));
		 gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, FloatBuffer.wrap(diffuse));
		 gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, FloatBuffer.wrap(specular));
		 gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION,FloatBuffer.wrap(lightPos));
		 gl.glEnable(GL.GL_LIGHT0);
		 // svetlo
         
		 gl.glFrustum(-1, 1, -1, 1, 1, 100);
		 VrmlViewPoint usedViewPoint=null;
		 
		 for (VrmlViewPoint viewPoint : viewpoints) {
			if(viewPoint.isUsed())
			{
				//TODO dokoncit
				usedViewPoint=viewPoint;
				break;
			}
		}
		 if (usedViewPoint==null){
		 gl.glTranslated(0, 0, -7);
		 }
		 else {
			 gl.glTranslated(usedViewPoint.getPositionX(), usedViewPoint.getPositionY(), usedViewPoint.getPositionZ());
			 gl.glRotated(usedViewPoint.getAngle(), usedViewPoint.getOrientationX(), usedViewPoint.getOrientationY(), usedViewPoint.getOrientationY());
		 }
		 //CanvasTransformations CT=new CanvasTransformations();
		 gl.glTranslated(xMove,yMove,zMove);
		 gl.glRotated(rotateXAngle, 1, 0, 0);
		 gl.glRotated(rotateYAngle, 0, 1, 0);
		 gl.glRotated(rotateZAngle, 0, 0, 1);
		 
		 if (activModel != null){
			 synchronized(activModel){
				 LinkedList<VrmlObject> objects = activModel.getModelComponents();
				 Iterator<VrmlObject> it = objects.iterator();
				 while (it.hasNext()){
					//VrmlObject obj = it.next();
					////////////////////
					Object obj = it.next();
					//TODO predpokladam ze toto sa tyka iba vykreslovania v pravej casti takze tu nema zmysel testovat ci je to typu viewpoint
					if (obj instanceof VrmlTransform){
						VrmlTransform transformation = (VrmlTransform)obj;
						transformation.applyTransformation(GLAutoDrawable);
					}
					/*else{
					VrmlObject object = (VrmlObject)obj;
				
					object.translateBack(gl); // od tialto
					object.rotateX(gl);
					object.rotateY(gl); 
					object.rotateZ(gl);
					object.translateToOrigins(gl); // po tialto to zabezpecuje lokalnu rotaciu objektu 
					object.draw(GLAutoDrawable);
					}*/
				 }
			 }
		 }
		 gl.glFlush();
	}

	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {	
	}
	
	public void init(GLAutoDrawable GLAutoDrawable) {
		 final GL gl = GLAutoDrawable.getGL();
         rotateYAngle = 0;
         rotateXAngle = 0;
         
	}

	public void reshape(GLAutoDrawable GLAutoDrawable, int x, int y, int width, int height) {
		
		final GL gl = GLAutoDrawable.getGL();
        int size,xMove=0,yMove=0;
        //FIXME ak to chceme inak tak neviem co treba spravit
        if(width>height) {
        	size=width;
        	yMove=(width-height)/2;
        }
        else {
        	size=height;
        	xMove=(height-width)/2;
        }
        gl.glViewport(-xMove, -yMove, size, size); // update the viewport
	
	}
	
	public void setActivModel(VrmlModel model){
		synchronized(this) {
			this.activModel = model;
		}
	}
	
	public VrmlModel getActivModel(){
		synchronized(this) {
			return activModel;
		}
	}

	public void incAngleX(){
		rotateXAngle += angleSize;
	}
	
	public void incAngleY(){
		rotateYAngle += angleSize;
	}
	
	public void incAngleZ(){
		rotateZAngle += angleSize;
	}
	
	public void moveRight(){
		xMove += moveSize;
	}
	public void moveLeft(){
		xMove -= moveSize;
	}
	public void moveUp(){
		yMove += moveSize;
	}
	public void moveDown(){
		yMove -= moveSize;
	}
	public void moveFront(){
		zMove += moveSize;
	}
	public void moveBack(){
		zMove -= moveSize;
	}
	
	public int getXMove(){
		return xMove;
	}
	
	public int getYMove(){
		return yMove;
	}
	
	public int getZMove(){
		return zMove;
	}

	public int getXAngle(){
		return rotateXAngle;
	}
	
	public int getYAngle(){
		return rotateYAngle;
	}
	
	public int getZAngle(){
		return rotateZAngle;
	}
	
	public int getRotSize(){
		return angleSize;
	}
	public void setRotSize(int size){
		angleSize = size;
	}
	public int getMoveSize(){
		return moveSize;
	}
	public void setMoveSize(int size){
		moveSize = size;
	}
}